#pragma once
/*
异步日志器模块
功能：1管理业务线程和日志线程的读取缓冲区的功能
2 提供同步和互斥的功能保证多线程下读取缓冲区和交换缓冲区的安全

所需成员：
1 消费者和生产者缓冲区
2 锁和条件变量实现同步和互斥
3 一个线程

*/
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <memory>
#include "LogBuffer.hpp"
using namespace MyLogBuffer;
using fun_t = std::function<void(LogBuffer &)>;
class UsyncLogger
{
public:
    // 线程创建即启动，给线程传入成员函数作为函数入口，参数this指针
    // 成员函数的地址需要加&符号
    UsyncLogger(const fun_t &fun)
        : _Isrunnig(true), _thread(&UsyncLogger::ThreadEntry, this), call_back(fun)
    {
    }

    void push(const std::string &Message)
    {
        // 1 向缓冲区加入数据前先加锁,锁不能拷贝，但是可以引用
        // RAII风格，创建加锁 销毁释放锁
        std::unique_lock<std::mutex> lock(_mutex);
        // 2 判断生产者缓冲区是否满了，满了则写入线程加入条件变量等待,并且释放锁
        if (!_Probuffer.IsWrite())
            _Procond.wait(lock);

        _Probuffer.push(Message);

        // std::cerr << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << Message << '\n';

        // 3 唤醒消费者输出日志
        _Concond.notify_one();
    }
    void Stop()
    {
        _Isrunnig = false;
        // 唤醒等待的日志线程，让他们准备退出
        _Concond.notify_all();
        _thread.join();//等待日志线程退出
    }
    ~UsyncLogger()
    {
        Stop();
    }

private:
    // 日志线程的入口函数
    void ThreadEntry()
    {
        // 处理缓冲区数据交换
        while (true)
        {
            // 1 加锁不能一边写入日志一边交换缓冲区的数据
            std::unique_lock<std::mutex> lock(_mutex);
            // 2 判断生产者的缓冲区有没有数据
            if (_Probuffer.IsRead())
            {
                _Probuffer.Swap(_Conbuffer);
                _Procond.notify_all(); // 交换缓冲区
                call_back(_Conbuffer); // 处理缓冲区数据
                _Conbuffer.Reset();    // 缓冲区复位
            }
            else
            { // 加入等待队列等待
                _Concond.wait(lock);
            }
            // 如果线程退出且当前缓冲区还有数据那么先处理完数据再退出
            if (_Probuffer.IsRead() && _Isrunnig == false)
                continue;
            if (!_Probuffer.IsRead() && _Isrunnig == false)
                break;
        }
    }

private:
    bool _Isrunnig;
    LogBuffer _Probuffer; // 业务线程缓冲区
    LogBuffer _Conbuffer; // 日志线程缓冲区

    fun_t call_back;
    std::mutex _mutex;
    std::mutex _pmutex;               // 副线程的锁
    std::condition_variable _Procond; // 业务线程的条件变量
    std::condition_variable _Concond; // 日志线程条件变量
    std::thread _thread;              // 线程对象创建日志线程
};